#include "cli.hpp"
#include "data.hpp"
#include "base.hpp"
#include <filesystem>
#include <fstream> // 用于文件操作
#include <gtest/gtest.h>
#include <iostream>

namespace commandtest {

namespace fs = std::filesystem;

// 函数：创建文件并写入数据
void createAndWriteFile(const fs::path &filePath, const std::string &content) {
    try {
        std::ofstream outFile(filePath);
        if (outFile) {
            outFile << content; // 写入数据
            outFile.close();
            // std::cout << "数据成功写入文件: " << filePath << '\n';
        } else {
            std::cerr << "文件创建失败: " << filePath << '\n';
        }
    } catch (const std::exception &e) {
        std::cerr << "写入文件时发生错误: " << e.what() << '\n';
    }
}

// 函数：创建文件夹
bool createFolder(const fs::path &folderPath) {
    try {
        if (!fs::exists(folderPath)) {
            if (fs::create_directories(folderPath)) {
                return true;
            } else {
                std::cerr << "文件夹创建失败: " << folderPath << '\n';
            }
        } else {
            return true;
        }
    } catch (const fs::filesystem_error &e) {
        std::cerr << "文件夹创建错误: " << e.what() << '\n';
    }
    return false;
}
// 向指定文件追加内容的函数
bool AppendToFile(const std::string &filepath, const std::string &content) {
    // 以追加模式打开文件
    std::ofstream ofs(filepath, std::ios::app);
    if (!ofs.is_open()) {
        return false;
    }
    ofs << content;
    return true;
}

std::string get_head_name() {
    std::ifstream inFile(HEAD, std::ios::in);
    std::string branch_name;
    inFile >> branch_name;
    inFile.close();
    if (branch_name.find("ref:") != std::string::npos && branch_name.size() > 4) {
        branch_name = branch_name.substr(4);
    }
    return branch_name;
}
std::vector<std::string> get_branch_names() {
    std::vector<std::string> branch_names;
    std::ifstream inFile(HEAD, std::ios::in);
    std::string branch_name;
    while (inFile >> branch_name) {
        if (branch_name.find("ref:") != std::string::npos && branch_name.size() > 4) {
            branch_name = branch_name.substr(4);
            branch_names.push_back(branch_name);
        } else {
            break;
        }
    }
    inFile.close();

    return branch_names;
}

std::string get_head_oid() {
    std::ifstream inFile(HEAD, std::ios::in);
    std::string branch_name;
    inFile >> branch_name;
    inFile.close();
    if (branch_name.find("ref:") != std::string::npos) {
        branch_name = branch_name.substr(4);
    } else {
        return branch_name;
    }
    inFile.open(HEADS_DIR / branch_name, std::ios::in);
    std::string now_head_oid;
    inFile >> now_head_oid;
    inFile.close();
    return now_head_oid;
}

class GlobalTestEnvironment : public ::testing::Environment {
  public:
    // 全局初始化
    void SetUp() override {
        if (check_file_exist(GIT_DIR, true)) {
            std::filesystem::remove_all(GIT_DIR);
        }
        std::vector<std::string> files = {"tagTest.txt", "branch_C_file.txt", "branch_B_file.txt",
                                          "commitTest.txt"};
        for (auto file : files) {
            if (check_file_exist(current_dir / file, false)) {
                std::filesystem::remove(current_dir / file);
            }
        }

        // 写入多个文件
        createAndWriteFile(current_dir / "aaa.txt", "file aaa.txt content\naaa测试内容\n");
        createAndWriteFile(current_dir / "bsa.txt", "file bsa.txt content\nbsa测试内容\n");

        // 创建文件夹并写入文件
        fs::path folderPath = current_dir / "testFolder";
        if (createFolder(folderPath)) {
            createAndWriteFile(folderPath / "testFile.txt",
                               "file testFile.txt content\ntestFile测试内容\n");
            createFolder(folderPath / "testFolder2");
            createAndWriteFile(folderPath / "testFolder2/testFile2.txt",
                               "file testFile2.txt content\ntestFile2测试内容\n");
        }
    }

    // 全局清理
    void TearDown() override {
        std::cout << "Global TearDown: 清理测试环境" << std::endl;
        // 清理全局资源
    }

  private:
};

TEST(CommandTest, INIT) {
    const char *argv[] = {"./mygit", "--init"};
    start_program(3, argv);
    EXPECT_TRUE(check_file_exist(GIT_DIR, true));
    EXPECT_TRUE(check_file_exist(HEAD, false));
    EXPECT_TRUE(check_file_exist(HEADS_DIR / "master", false));
}

TEST(CommandTest, HashObject) {
    const char *argv[]  = {"./mygit", "--hash-object", "bsa.txt"};
    std::string bsaHash = "a2a38cb46510b5ca95d927e31a13793e1fd51";
    start_program(4, argv);
    EXPECT_TRUE(check_file_exist(GIT_DIR, true));
    EXPECT_TRUE(check_file_exist(OBJECT_DIR, true));
    EXPECT_TRUE(check_file_exist(OBJECT_DIR / bsaHash, false));
    EXPECT_TRUE(check_file_exist(REFS_DIR, true));
    EXPECT_TRUE(check_file_exist(TAGS_DIR, true));
    EXPECT_TRUE(check_file_exist(HEADS_DIR, true));
    // 开始捕获 std::cout 的输出
    testing::internal::CaptureStdout();
    const char *argv2[] = {
        "./mygit",
        "--cat-file",
        bsaHash.c_str(),
    };
    start_program(4, argv2);
    std::string output = testing::internal::GetCapturedStdout();
    EXPECT_TRUE(output.find("file bsa.txt content\nbsa测试内容\n") != std::string::npos);
}

TEST(CommandTest, Commit) {
    const char *argv_add[] = {"./mygit", "--add", "."};
    EXPECT_EQ(start_program(4, argv_add), 0);
    const char *argv[] = {"./mygit", "--commit", "node A start"};
    EXPECT_EQ(start_program(4, argv), 0);

    std::string old_head_oid;

    std::ifstream inFile(HEADS_DIR / "master", std::ios::in);
    inFile >> old_head_oid;
    inFile.close();
    EXPECT_TRUE(check_file_exist(OBJECT_DIR / old_head_oid, false));
    EXPECT_EQ(start_program(4, argv_add), 0);
    EXPECT_EQ(start_program(4, argv), 0);

    createAndWriteFile(current_dir / "commitTest.txt", "info test commit message\n");
    EXPECT_EQ(start_program(4, argv_add), 0);
    const char *argv2[] = {"./mygit", "--commit", "node B"};
    EXPECT_EQ(start_program(4, argv2), 0);
    inFile.open(HEADS_DIR / "master", std::ios::in);
    std::string new_head_oid;
    inFile >> new_head_oid;
    inFile.close();
    EXPECT_TRUE(check_file_exist(OBJECT_DIR / new_head_oid, false));
    inFile.open(OBJECT_DIR / new_head_oid, std::ios::in);
    CommitMessage commit_message;
    inFile >> commit_message;
    inFile.close();
    EXPECT_EQ(old_head_oid, commit_message.parent_oid_vct.front());
}
TEST(CommandTest, Log) {
    // 开始捕获 std::cout 的输出
    testing::internal::CaptureStdout();
    const char *argv[] = {"./mygit", "--log"};
    EXPECT_EQ(start_program(3, argv), 0);
    std::string output = testing::internal::GetCapturedStdout();
    EXPECT_TRUE(output.find("branch master") != std::string::npos);
    EXPECT_TRUE(output.find("commit message node B") != std::string::npos);
    EXPECT_TRUE(output.find("parent start_parent") != std::string::npos);
    EXPECT_TRUE(output.find("commit message node A start") != std::string::npos);
}

TEST(CommandTest, Checkout) {

    std::string now_head_oid = get_head_oid();

    EXPECT_TRUE(check_file_exist(OBJECT_DIR / now_head_oid, false));
    CommitMessage commit_message = get_commit_message(now_head_oid);
    std::string oid              = commit_message.parent_oid_vct.front();
    oid                          = "oid=" + oid;

    const char *argv[] = {"./mygit", "--checkout", oid.c_str()};
    EXPECT_EQ(start_program(4, argv), 0);
    EXPECT_TRUE(check_file_exist(current_dir / "aaa.txt", false));
    EXPECT_TRUE(check_file_exist(current_dir / "bsa.txt", false));
    EXPECT_TRUE(check_file_exist(current_dir / "testFolder", true));
    EXPECT_TRUE(check_file_exist(current_dir / "testFolder/testFile.txt", false));
    EXPECT_TRUE(!check_file_exist(current_dir / "commitTest.txt", false));
}

TEST(CommandTest, TAG) {
    std::string now_head_oid = get_head_oid();
    const char *argv[]       = {"./mygit", "--tag", "start_tag", now_head_oid.c_str()};
    EXPECT_EQ(start_program(5, argv), 0);
    EXPECT_TRUE(check_file_exist(TAGS_DIR / "start_tag", false));
    EXPECT_EQ(get_tag_oid("start_tag"), now_head_oid);

    createAndWriteFile(current_dir / "tagTest.txt", "info test tag message\n");
    const char *argv_add[] = {"./mygit", "--add", "."};
    EXPECT_EQ(start_program(4, argv_add), 0);
    const char *argv3[] = {"./mygit", "--commit", "tag commit"};
    EXPECT_EQ(start_program(4, argv3), 0);

    now_head_oid        = get_head_oid();
    const char *argv4[] = {"./mygit", "--tag", "end_tag", now_head_oid.c_str()};
    EXPECT_EQ(start_program(5, argv4), 0);

    EXPECT_TRUE(check_file_exist(TAGS_DIR / "end_tag", false));

    const char *argv5[] = {"./mygit", "--checkout", "tag=start_tag"};
    EXPECT_EQ(start_program(4, argv5), 0);
    EXPECT_TRUE(!check_file_exist(current_dir / "tagTest.txt", false));
}

TEST(CommandTest, BRANCH) {
    const char *argv[] = {"./mygit", "--branch", "test_branch"};
    EXPECT_EQ(start_program(4, argv), 0);
    EXPECT_TRUE(check_file_exist(HEADS_DIR / "test_branch", false));
    EXPECT_EQ("test_branch", get_head_name());
    std::ifstream inFile(HEADS_DIR / "test_branch", std::ios::in);
    std::string now_head_oid;
    inFile >> now_head_oid;
    inFile.close();

    createAndWriteFile(current_dir / "tagTest.txt", "info test tag message\n");
    const char *argv_add[] = {"./mygit", "--add", "."};
    EXPECT_EQ(start_program(4, argv_add), 0);
    const char *argv2[] = {"./mygit", "--commit", "node B"};
    EXPECT_EQ(start_program(4, argv2), 0);
    EXPECT_TRUE(check_file_exist(HEADS_DIR / "test_branch", false));
    EXPECT_EQ("test_branch", get_head_name());

    inFile.open(HEADS_DIR / "test_branch", std::ios::in);
    std::string new_head_oid;
    inFile >> new_head_oid;
    inFile.close();

    EXPECT_NE(new_head_oid, now_head_oid);
}

TEST(CommandTest, BRANCH_CHECKOUT) {

    const char *argv2[] = {"./mygit", "--branch", "branch_B"};
    ASSERT_EQ(start_program(4, argv2), 0);
    ASSERT_EQ("branch_B", get_head_name());

    const char *argv3[] = {"./mygit", "--branch", "branch_C"};
    ASSERT_EQ(start_program(4, argv3), 0);
    ASSERT_EQ("branch_C", get_head_name());

    createAndWriteFile(current_dir / "branch_C_file.txt", "info branch_C file message\n");
    const char *argv_add[] = {"./mygit", "--add", "."};
    EXPECT_EQ(start_program(4, argv_add), 0);
    const char *argv4[] = {"./mygit", "--commit", "node C in branch C"};
    ASSERT_EQ(start_program(4, argv4), 0);

    const char *argv5[] = {"./mygit", "--checkout", "branch=branch_B"};
    ASSERT_EQ(start_program(4, argv5), 0);
    ASSERT_EQ("branch_B", get_head_name());
    ASSERT_TRUE(!check_file_exist(current_dir / "branch_C_file.txt", false));

    createAndWriteFile(current_dir / "branch_B_file.txt", "info branch_B file message\n");

    EXPECT_EQ(start_program(4, argv_add), 0);

    const char *argv6[] = {"./mygit", "--commit", "node B in branch B"};
    ASSERT_EQ(start_program(4, argv6), 0);
    ASSERT_EQ("branch_B", get_head_name());
}

TEST(CommandTest, BRNACH_LIST) {
    // 开始捕获 std::cout 的输出
    testing::internal::CaptureStdout();
    const char *argv[] = {"./mygit", "--branch", "-a"};
    EXPECT_EQ(start_program(4, argv), 0);
    // 获取捕获到的输出内容
    std::string output = testing::internal::GetCapturedStdout();
    EXPECT_TRUE(output.find("all branch :") != std::string::npos);
    EXPECT_TRUE(output.find("master") != std::string::npos);
    EXPECT_TRUE(output.find("test_branch") != std::string::npos);
    EXPECT_TRUE(output.find("* branch_B") != std::string::npos);
    EXPECT_TRUE(output.find("branch_C") != std::string::npos);
}

TEST(CommandTest, RESET) {
    InitCommand init_command;
    std::string branch_B_head_oid = init_command.get_head_oids().front();
    EXPECT_TRUE(check_file_exist(current_dir / "branch_B_file.txt", false));

    const char *argv[] = {"./mygit", "--checkout", "branch=master"};
    EXPECT_EQ(start_program(4, argv), 0);
    EXPECT_TRUE(!check_file_exist(current_dir / "branch_B_file.txt", false));
    EXPECT_EQ("master", get_head_name());

    const char *argv2[] = {"./mygit", "--reset", branch_B_head_oid.c_str()};
    EXPECT_EQ(start_program(4, argv2), 0);
    EXPECT_TRUE(check_file_exist(current_dir / "branch_B_file.txt", false));
    EXPECT_EQ("master", get_head_name());
}

TEST(CommandTest, SHOW) {
    // 开始捕获 std::cout 的输出
    testing::internal::CaptureStdout();

    const char *argv[] = {"./mygit", "--show"};
    EXPECT_EQ(start_program(3, argv), 0);

    // 获取捕获到的输出内容
    std::string output = testing::internal::GetCapturedStdout();
    // std::cout << output << std::endl;
    // 检查输出内容
    EXPECT_TRUE(output.find("branch branch_B") != std::string::npos);
    EXPECT_TRUE(output.find("node B in branch B") != std::string::npos);

    EXPECT_TRUE(output.find("new file: branch_B_file.txt") != std::string::npos);

    AppendToFile(current_dir / "testFolder/testFile.txt", "append test message\n");
    remove_all(current_dir / "bsa.txt");

    const char *argv_add[] = {"./mygit", "--add", "."};
    EXPECT_EQ(start_program(4, argv_add), 0);

    const char *argv2[] = {"./mygit", "--commit", "delete and modify file"};
    EXPECT_EQ(start_program(4, argv2), 0);

    // 开始捕获 std::cout 的输出
    testing::internal::CaptureStdout();
    EXPECT_EQ(start_program(3, argv), 0);

    // 获取捕获到的输出内容
    output = testing::internal::GetCapturedStdout();
    // std::cout << output << std::endl;
    EXPECT_TRUE(output.find("deleted file: bsa.txt") != std::string::npos);
    EXPECT_TRUE(output.find("modified file: testFolder/testFile.txt") != std::string::npos);
}

TEST(CommandTest, DIFF) {
    AppendToFile(current_dir / "testFolder/testFile.txt", "append test message for diff\n");
    createAndWriteFile(current_dir / "tagTest.txt", "info diff tag message\n");
    // 开始捕获 std::cout 的输出
    testing::internal::CaptureStdout();
    const char *argv[] = {"./mygit", "--diff"};
    EXPECT_EQ(start_program(3, argv), 0);
    // 获取捕获到的输出内容
    std::string output = testing::internal::GetCapturedStdout();
    // std::cout << output << std::endl;
    EXPECT_TRUE(output.find("+info test tag message") != std::string::npos);
    EXPECT_TRUE(output.find("-append test message for diff") != std::string::npos);
}

TEST(CommandTest, MERGE) {
    createAndWriteFile(current_dir / "branch_C_file.txt", "info branch_master file message\n");
    const char *argv_add[] = {"./mygit", "--add", "."};
    EXPECT_EQ(start_program(4, argv_add), 0);
    const char *argv[] = {"./mygit", "--commit", "node D in master"};
    EXPECT_EQ(start_program(4, argv), 0);

    const char *argv2[] = {"./mygit", "--merge", "branch_C"};
    EXPECT_EQ(start_program(4, argv2), 0);
    EXPECT_TRUE(check_file_exist(current_dir / "branch_C_file.txt", false));
    EXPECT_TRUE(check_file_exist(current_dir / "branch_B_file.txt", false));
    EXPECT_EQ("master", get_head_name());
    auto branch_names = get_branch_names();
    EXPECT_EQ(branch_names.size(), 2);
    EXPECT_EQ(branch_names.front(), "master");
    EXPECT_EQ(branch_names.back(), "branch_C");

    std::ifstream inFile(current_dir / "tagTest.txt", std::ios::in);
    std::string start_info;
    inFile >> start_info;
    inFile.close();
    EXPECT_EQ(start_info, "<<<<<<<");
    InitCommand init_command;
    EXPECT_EQ(init_command.get_head_oids().size(), 2);
    EXPECT_EQ(start_program(4, argv_add), 0);
    const char *argv3[] = {"./mygit", "--commit", "merge branch_C"};
    EXPECT_EQ(start_program(4, argv3), 0);
    EXPECT_EQ(init_command.get_head_oids().size(), 1);
}

TEST(CommandTest, MERGE_BASE) {
    // 开始捕获 std::cout 的输出
    testing::internal::CaptureStdout();
    const char *argv[] = {"./mygit", "--merge-base", "branch_B", "branch_C"};
    EXPECT_EQ(start_program(5, argv), 0);
    // 获取捕获到的输出内容
    std::string output = testing::internal::GetCapturedStdout();
    // std::cout << output << std::endl;
    InitCommand init_command;
    EXPECT_TRUE(output.find(init_command.get_ref_oid("test_branch")) != std::string::npos);
}

TEST(CommandTest, MERGE_FAST_FORWARD) {
    InitCommand init_command;
    std::string master_commit_oid = init_command.get_head_oids().front();
    const char *argv[]            = {"./mygit", "--checkout", "branch=test_branch"};
    EXPECT_EQ(start_program(4, argv), 0);
    EXPECT_EQ("test_branch", get_head_name());
    EXPECT_TRUE(!check_file_exist(current_dir / "branch_C_file.txt", false));

    const char *argv2[] = {"./mygit", "--merge", "master"};
    EXPECT_EQ(start_program(4, argv2), 0);
    EXPECT_EQ("test_branch", get_head_name());
    EXPECT_TRUE(check_file_exist(current_dir / "branch_C_file.txt", false));
    EXPECT_EQ(master_commit_oid, init_command.get_head_oids().front());

    EXPECT_EQ(init_command.get_ref_oid("test_branch"), init_command.get_ref_oid("master"));
}

TEST(CommandTest, ADD) {
    const char *argv[] = {"./mygit", "--add", "testFolder"};
    EXPECT_EQ(start_program(4, argv), 0);
    EXPECT_TRUE(check_file_exist(INDEX_FILE, false));
    std::ifstream inFile(INDEX_FILE, std::ios::in);
    if (inFile.is_open()) {
        std::string line_one, line_two;
        std::getline(inFile, line_one);
        std::getline(inFile, line_two);
        inFile.close();
        EXPECT_TRUE(line_one.find("testFolder/testFile.txt") != std::string::npos);
        EXPECT_TRUE(line_two.find("testFolder/testFolder2/testFile2.txt") != std::string::npos);
    } else {
        inFile.close();
        assert(false);
    }
}

TEST(CommandTest, STATUS) {
    // 开始捕获 std::cout 的输出
    testing::internal::CaptureStdout();
    AppendToFile(current_dir / "aaa.txt", "append status message\n");

    const char *argv[] = {"./mygit", "--status"};
    EXPECT_EQ(start_program(3, argv), 0);
    // 获取捕获到的输出内容
    std::string output = testing::internal::GetCapturedStdout();
    // std::cout << output << std::endl;
    EXPECT_TRUE(output.find("now_branch: test_branch") != std::string::npos);
    EXPECT_TRUE(output.find("modified file: aaa.txt") != std::string::npos);

    testing::internal::CaptureStdout();
    const char *argv2[] = {"./mygit", "--add", "aaa.txt"};
    EXPECT_EQ(start_program(4, argv2), 0);
    EXPECT_EQ(start_program(3, argv), 0);
    // 获取捕获到的输出内容
    output = testing::internal::GetCapturedStdout();
    // std::cout << output << std::endl;
    EXPECT_TRUE(output.find("staged files:\n  modified file: aaa.txt") != std::string::npos);
}

TEST(CommandTest, DIFF_STAGED) {
    testing::internal::CaptureStdout();
    const char *argv[] = {"./mygit", "--diff", "--staged"};
    EXPECT_EQ(start_program(4, argv), 0);
    // 获取捕获到的输出内容
    std::string output = testing::internal::GetCapturedStdout();
    // std::cout << output << std::endl;
    EXPECT_TRUE(output.find("+file aaa.txt content") != std::string::npos);
    EXPECT_TRUE(output.find("-append status message") != std::string::npos);
}

} // namespace commandtest

int main(int argc, char **argv) {

    std::string current_dir_str = std::filesystem::current_path().string();
    if (current_dir_str.find("test_dir") == std::string::npos) {
        std::cerr << "请在test_dir下运行测试" << std::endl;
        return -1; // 确保在test_dir运行数据
    }

    ::testing::InitGoogleTest(&argc, argv);

    // 注册全局环境
    ::testing::AddGlobalTestEnvironment(new commandtest::GlobalTestEnvironment());

    return RUN_ALL_TESTS();
}